﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Data;
using PTA.Db;
using System.Linq;
using System.Web.UI.WebControls;
namespace PTA.Auth
{
    /// <summary>
    /// 权限管理
    /// </summary>
    public class AuthManage
    {
        private static AuthManage instance = null;
        private Dictionary<string, MenuTree> menus = new Dictionary<string, MenuTree>();
        private Dictionary<string, KeyValuePair<string, int>> menuitemvalues = new Dictionary<string, KeyValuePair<string, int>>();
        private List<IRole> roles = new List<IRole>();
        internal Type privilegeType = null;

        internal static AuthManage Instance
        {
            get
            {
                return instance;
            }
        }

        /// <summary>
        /// 创建权限管理实体
        /// </summary>
        /// <param name="db">数据库访问组件</param>
        /// <param name="menuxml">菜单文件</param>
        /// <param name="privilegeType">权限类型</param>
        /// <param name="roles">系统所有角色列表</param>
        public static AuthManage Create(string menuxml, Type privilegeType, List<IRole> roles)
        {
            instance = new AuthManage();
            foreach (int e in Enum.GetValues(privilegeType))
            {
                if (e < 100000)
                {
                    throw new Exception("功能点的值要大于100000！");
                }
            }
            instance.privilegeType = privilegeType;
            instance.roles = roles;
            System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
            doc.LoadXml(menuxml);
            foreach (System.Xml.XmlNode menunode in doc.SelectNodes("MenuList/Menu"))
            {
                MenuTree menu = new MenuTree();
                instance.menus.Add(menunode.Attributes["Name"].InnerText, menu);
                foreach (System.Xml.XmlNode menuitemxml in menunode.SelectNodes("MenuItem"))
                {
                    MenuNode menuitem = new MenuNode();
                    LoadMenuItem(menuitem, menuitemxml, instance.menuitemvalues);
                    menu.Nodes.Add(menuitem);
                }
            }
            return instance;
        }

        private static void LoadMenuItem(MenuNode menuitem, System.Xml.XmlNode menuitemnode, Dictionary<string, KeyValuePair<string, int>> menuitemvalues)
        {
            #region 设置属性
            var url = menuitemnode.Attributes["Url"];
            if (url != null)
            {
                menuitem.Url = url.Value;
                if (string.IsNullOrWhiteSpace(menuitem.Url))
                {
                    throw new Exception("菜单URL不能为空。");
                }
            }
            else
            {
                throw new Exception("未设置菜单URL。");
            }

            var authattr = menuitemnode.Attributes["AuthUrl"];
            if (authattr != null)
            {
                menuitem.AuthUrl = authattr.InnerText;
            }
            else
            {
                menuitem.AuthUrl = menuitem.Url;
            }

            var cssattr = menuitemnode.Attributes["CssClass"];
            if (cssattr != null)
            {
                menuitem.CssClass = cssattr.InnerText;
            }

            var textattr = menuitemnode.Attributes["Text"];
            if (textattr != null)
            {
                menuitem.Text = textattr.Value;
            }
            var valueattr = menuitemnode.Attributes["Value"];
            if (valueattr != null)
            {
                if (Convert.IsInt(valueattr.Value) && Convert.ToInt32(valueattr.Value) < 100000)
                {
                    if (menuitemvalues.ContainsKey(menuitem.Url))
                    {
                        throw new Exception(string.Format("路径为{0}的菜单重复设置了权限值", menuitem.Url));
                    }
                    menuitem.Value = PTA.Convert.ToInt32(valueattr.Value);
                }
                else
                {
                    throw new Exception("菜单项的值要小于100000");
                }
            }

            var enableattr = menuitemnode.Attributes["Enabled"];
            if (enableattr != null)
            {
                menuitem.Enabled = Convert.ToBoolean(enableattr.InnerText);
            }

            var imageurlattr = menuitemnode.Attributes["ImageUrl"];
            if (imageurlattr != null)
            {
                menuitem.ImageUrl = imageurlattr.InnerText;
            }

            var targetattr = menuitemnode.Attributes["Target"];
            if (targetattr != null)
            {
                menuitem.Target = targetattr.InnerText;
            }

            var tooltipattr = menuitemnode.Attributes["ToolTip"];
            if (tooltipattr != null)
            {
                menuitem.ToolTip = tooltipattr.InnerText;
            }
            #endregion

            menuitemvalues.Add(menuitem.Url, new KeyValuePair<string, int>(menuitem.AuthUrl, Convert.ToInt32(menuitem.Value)));

            if (menuitemnode.SelectNodes("MenuItem").Count > 0)
            {
                foreach (System.Xml.XmlNode childmenuitemxml in menuitemnode.SelectNodes("MenuItem"))
                {
                    MenuNode chilemenuitem = new MenuNode();
                    menuitem.Add(chilemenuitem);
                    LoadMenuItem(chilemenuitem, childmenuitemxml, menuitemvalues);
                }
            }
        }

        /// <summary>
        /// 角色列表,当系统中的角色更新时,需要修改角色列表
        /// </summary>
        public List<IRole> Roles
        {
            get
            {
                return roles;
            }
        }

        #region 菜单及功能点列表

        /// <summary>
        /// 获取系统所有功能点
        /// </summary>
        /// <returns></returns>
        public Menu PrivilegeList
        {
            get
            {
                Array values = System.Enum.GetValues(privilegeType);
                Menu result = new Menu();
                foreach (var privilege in privilegeType.GetFields())
                {
                    if (privilege.IsDefined(typeof(PrivilegeItemAttribute), true))
                    {
                        PrivilegeItemAttribute attr = (PrivilegeItemAttribute)privilege.GetCustomAttributes(typeof(PrivilegeItemAttribute), true)[0];
                        #region 处理分组
                        MenuItemCollection childmenus = result.Items;
                        MenuItem current = null;
                        foreach (var name in attr.Parent.Split('|'))
                        {
                            current = null;
                            foreach (MenuItem child in childmenus)
                            {
                                if (child.Text == name)
                                {
                                    current = child;
                                    break;
                                }
                            }
                            if (current == null)
                            {
                                current = new MenuItem(name, "0");
                                childmenus.Add(current);
                            }
                            childmenus = current.ChildItems;
                        }
                        #endregion
                        current.ChildItems.Add(new MenuItem() { Text = privilege.Name, Value = Convert.ToInt32(privilege.GetValue(values)).ToString() });
                    }
                }
                return result;
            }
        }

        public Dictionary<string, MenuTree> MenuList
        {
            get
            {
                return this.menus;
            }
        }
        #endregion

        #region 权限检查
        /// <summary>
        /// 检查指定角色列表里是否包含参数指定的权限
        /// </summary>
        /// <param name="privilege">要检查权限或菜单的值(0值返回true)</param>
        /// <param name="ea">要检查的角色列表</param>
        /// <returns>返回一个值,指示权限实例是否有特定的权限或菜单</returns>
        public virtual bool CheckAuth(int privilege, IEntityAuth ea)
        {
            if (privilege == 0)
            {
                return true;
            }
            if (ea.ExcludePrivilegeList != null && ea.ExcludePrivilegeList.Contains(privilege))
            {
                //如果排除的权限中有要检查的功能点,则用户没有权限
                return false;
            }
            return (ea != null && ea.IncludePrivilegeList != null && ea.IncludePrivilegeList.Contains(privilege)) || (this.roles != null && ea != null && ea.RoleList != null && this.roles.Exists(r => ea.RoleList.Contains(r.RoleId) && r.PrivilegeList.Contains(privilege)));
        }

        /// <summary>
        /// 检查菜单权限
        /// </summary>
        /// <param name="url">菜单的URL(空字符串返回true)</param>
        /// <param name="ea"></param>
        /// <returns></returns>
        public virtual bool CheckAuth(string url, IEntityAuth ea)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return true;
            }
            if (menuitemvalues.ContainsKey(url))
            {
                var p = menuitemvalues[url];
                if (p.Key.Equals(url, StringComparison.OrdinalIgnoreCase))
                {
                    if (p.Value == 0)
                    {
                        return true;
                    }
                    return CheckAuth(p.Value, ea);
                }
                else
                {
                    return CheckAuth(p.Key, ea);
                }

            }
            return false;
        }

        /// <summary>
        /// 获取系统菜单(当roles为null,返回这个菜单的所有子项)
        /// </summary>
        /// <param name="name">菜单名称</param>
        /// <param name="roles">角色列表</param>
        /// <returns>返回根据角色找到的菜单列表</returns>
        public virtual MenuTree GetMenu(string name, IEntityAuth ea)
        {

            MenuTree result = GetMenu(name);
            //查找所有可用的菜单值
            List<int> menuvalues = new List<int>(ea.IncludePrivilegeList.FindAll(v => v < 10000 && ea.ExcludePrivilegeList.NotContains(v)));

            //根据角色信息，找到所有的菜单节点
            this.roles.FindAll(item => ea.RoleList.Contains(item.RoleId)).ForEach(r => r.PrivilegeList.ForEach(m => { if (m < 10000 && menuvalues.NotContains(m) && ea.ExcludePrivilegeList.NotContains(m)) menuvalues.Add(m); }));

            FilterMenu(result.Nodes, menuvalues);
            return result;
        }

        public virtual MenuTree GetMenu(string name)
        {
            MenuTree source = menus[name];
            if (source == null)
            {
                throw new Exception(string.Format("未找到名称为[{0}]的菜单", name));
            }
            return source.Clone();
        }

        /// <summary>
        /// 过虑节点，只选择参数指定的节点及其父节点
        /// </summary>
        /// <param name="menus"></param>
        /// <param name="values"></param>
        private void FilterMenu(List<MenuNode> menus, List<int> values)
        {
            for (int i = menus.Count - 1; i >= 0; i--)
            {
                //如果存在子节点,先检查子节点是否有权限,然后再处理当前节点,如果删除了所有的子节点,并且当前节点不在权限中,删除当前节点
                if (menus[i].ChildNodes.Count > 0)
                {
                    FilterMenu(menus[i].ChildNodes, values);
                }
                if (!values.Contains(Convert.ToInt32(menus[i].Value)))
                {
                    menus.RemoveAt(i);
                }
            }
        }
        #endregion
    }
}
